#include <stdlib.h>

#include "lib.h"
#include "BonusBingoSql.h"
#include "BonusBingoGame.h"
#include "BonusBingoGameResult.h"
#include "BOnusBingoCard.h"
#include "BonusBingoSubGame.h"

#include "odbc.h"
#include "missinggamenums.h"

BonusBingoSQL::BonusBingoSQL(TrackSQL* tracker)
{
    //We're going to assume this pointer is always valid
    ASSERT( tracker );

    this->tracker = tracker;
#ifdef USE_MISSINGGAMENUMS
	//missing game nums deleted in TrackSQL::~TrackSQL
	missinggamenums = new MissingGameNums(this, "pub_getTableIdentityKeno");
#endif
}

int32
BonusBingoSQL::createGame(int32 casinoId,
	const PID& pid, bool8 forMoney, SESSIONID sessionId, int32 compFactor,
	GameID gid, int32* balance, BonusBingoGame *game, bool8 newGroup)
{
	ODBCConnection* conn;
	ODBCQuery* query;
	bool8 ok = false;

	conn = tracker->getConn();
	if (!conn)
        return TrackSQL::NoConnection;

	query = conn->query();
	if (query)
	{
		query->define("exec pub_CreateGameBonusBingo ");
		query->addParam(casinoId);
		query->addParam(pid);
		query->addParam((bool8)(forMoney != 0));
		query->addParam(sessionId, ODBCQuery::NOQUOTES);
		query->addParam(compFactor);
		query->addParam(game->getTotalBet(), ODBCQuery::MONEY);
		query->addParam(game->getCardsPlayed());
		query->addParam(game->getMultiplierValue());

		if (query->exec())
		{
			while (query->getRow())
			{
				ok =
					query->getData(1, gid, GAMEID_SIZE) &&
					query->getData(2, balance, ODBCQuery::MONEY);
					//The old keno game appears to throw this away
					//so I'm not going to bother with it at all.
					/*&& query->getData(3, comp);*/
			}
		}
		delete query;
	}

	if (ok)
	{
		#ifdef USE_MISSINGGAMENUMS
		int32 gamenum = atol(gid);
		if (missinggamenums)
				missinggamenums->setGameNum(gamenum);
		#endif
	}

	if(ok)
	{
        ok = false;

		BonusBingoCard *card;
		int32 cardid;
		// now add all the cards to the database
		for(int32 i = 0; i < BonusBingo::NUM_MAX_CARDS; i++)
		{
			card = game->getCard(i);

			query = conn->query();
			if(query)
			{
				query->define("exec pub_BonusBingoCreateCard ");
				query->addParam(gid, ODBCQuery::NOQUOTES);

                //Unplayed cards show up in the data base as just having no bet
                query->addParam(game->getBetForCard(i), ODBCQuery::MONEY);

                // the card number (for display on the report)
                query->addParam( (unsigned char) (i+1) );

				//Now, add each value
				for( int row = 0 ; row < 5 ; ++row )
				{
					for( int col = 0 ; col < 5 ; ++col )
					{
						//The database excludes the free cell
						if ( col == 2 && row == 2 )
						{
							continue;
						}
						query->addParam( card->getValue( col, row ) );
					}
				}

				if (query->exec())
				{
					while (query->getRow())
					{
						ok = query->getData(1, &cardid);
						if ( ok )
						{
							//remember this for later.
							card->setID( cardid );
						}
					}
				}
			}
			delete query;

			if(!ok)	break;
		}
	}

	delete conn;

	return ok ? TrackSQL::NoError : TrackSQL::ExecFailed;
}

int32
BonusBingoSQL::gameDone(int32 casinoId, const PID& pid, bool8 forMoney, GameID gid,
			int32 balanceAdjust, int32 *balance, int32 *comp, BonusBingoGameResult* results)
{    
    typedef BonusBingoGameResult::BallSet BallSet;

	ODBCConnection* conn;
	ODBCQuery* query;
	bool8 ok = true;

	conn = tracker->getConn();
	if (!conn)
		return TrackSQL::NoConnection;

	//Record the wins for each played card
	for ( int cardIndex = 0;
	cardIndex < BonusBingo::NUM_MAX_CARDS;
	++cardIndex )
	{
		BonusBingoCard* pCard = results->getGame().getCard( cardIndex );			
		
		if ( pCard->isActive() )
		{				
			for( int win = 0, totalWins = results->winsForCard( cardIndex );
				 win < totalWins && ok; 
				 ++win)						
			{
				query = conn->query();
				
				if ( query )
				{
					query->define( "exec pub_BonusBingoAddWin " );
					query->addParam( gid, ODBCQuery::NOQUOTES );
					query->addParam( pCard->getID() );
					query->addParam( (uint8) results->getWinPattern( cardIndex, win ) );
                    query->addParam( results->getWinPayout( cardIndex, win ), ODBCQuery::MONEY );
					
					ok = query->exec();
				}
				
				delete query;
			}
		}
	}
	
	
	if ( ok )
	{		
        ok = false;

		query = conn->query();

		if (query)
		{
			query->define("exec pub_BonusBingoGameDone ");
			query->addParam(casinoId);
			query->addParam(pid);
			query->addParam((bool8)(forMoney != 0));
			query->addParam(gid, ODBCQuery::NOQUOTES);
			
			// add the picks
			const BallSet& balls = results->getSelectedBalls();
			
			for(BallSet::const_iterator it( balls.begin() ), end( balls.end() );
			it != end; ++it )
			{
				query->addParam(*it);
			}
			
			//add the selected multiplier card's ID            
            query->addParam( (int32)results->getGame().getMultipliedCardID() );
            
            //and the joker value
            query->addParam( results->getJokerValue(), ODBCQuery::MONEY );
			
			//and the balance adjustment
			query->addParam(balanceAdjust, ODBCQuery::MONEY);
			
			if (query->exec())
			{
				while (query->getRow())
				{
					ok = query->getData(1, balance, ODBCQuery::MONEY) &&
						query->getData(2, comp);
				}
			}
			delete query;
		}
	}
	delete conn;

	return ok ? TrackSQL::NoError : TrackSQL::ExecFailed;
}

int32
BonusBingoSQL::getPayTable(uint8 *paytable)
{
	ODBCConnection* conn;
	ODBCQuery* query;
	bool8 ok = false;

	*paytable = 0;

	conn = tracker->getConn();
	if (!conn)
		return TrackSQL::NoConnection;

	query = conn->query();
	if (query)
	{
		query->define("exec pub_KenoGetOptions");
		if (query->exec())
		{
			while (query->getRow())
			{
				ok = query->getData(1, paytable);
			}
		}
		delete query;
	}

	delete conn;

	return ok ? TrackSQL::NoError : TrackSQL::ExecFailed;
}

int32
BonusBingoSQL::getPlayerLimits(const PID& pid, int32 gameID, uint8 machId,
	int32* compFactor)
{
	ODBCConnection* conn;
	ODBCQuery* query;
	bool8 ok = false;

	conn = tracker->getConn();
	if (!conn)
		return TrackSQL::NoConnection;

	query = conn->query();
	if (query)
	{
		query->define("exec pub_lim_getPlayerLimits ");
		query->addParam(pid);
		query->addParam(gameID);
		query->addParam(machId, ODBCQuery::BINARY);

		if (query->exec())
		{
			while (query->getRow())
			{
				ok = query->getData(4, compFactor);
			}
		}
		delete query;
	}
	delete conn;

	return ok ? tracker->isGameEnabled(gameID,machId) : TrackSQL::ExecFailed;
}

//psharpless  11/3/06  case#: 9360
//Bonus Bingo denomination amounts are no longer hard coded
int32
BonusBingoSQL::getDenominations(int32 *DenomValues)
{
	ODBCConnection* conn;
	ODBCQuery* query;
	bool8 ok = false;

    localCurrency lc;

	ok = tracker->getLocale(&lc) == TrackSQL::NoError;

    if (!ok)
    {
        return ok;
    }

	conn = tracker->getConn();
	if (!conn)
		return TrackSQL::NoConnection;

	query = conn->query();
	if (query)
	{
		query->define("exec pub_BonusBingoGetDenominations ");
        query->addParam(lc.int_curr_symbol);

		if (query->exec())
		{
            int nCount=0;

            // get the 5 (and ONLY 5) denomination values.
            while (ok && nCount<5 && query->getRow())
            {
                ok = query->getData(1, &DenomValues[nCount++]);                    
            }

            if(nCount!=5)
            {
                ok=false;
            }
		}
		delete query;
	}
	delete conn;

	return ok ? TrackSQL::NoError : TrackSQL::ExecFailed;
}

int32
BonusBingoSQL::getSavedGame(const PID& pid, bool8 forMoney, GameID gameID, bool8* completed, 
                           BonusBingoGame*& game, BonusBingoGameResult*& result)
{
	ODBCConnection* conn;
	ODBCQuery* query;

	bool8 ok = false;

	conn = tracker->getConn();
	
    if (!conn)
		return TrackSQL::NoConnection;

    int32 multipliedCardID = 0;

	query = conn->query();
	if (query)
	{
		query->define("exec pub_BonusBingoGetSavedGames ");
		query->addParam(pid);
        query->addParam((bool8)(forMoney != 0));

		if (query->exec())
		{
            int32 totalBet = 0;
            int32 cardsPlayed = 0;
            uint8 multiplierValue = 0;

			if (query->getRow()) //one row, no loop
			{
				ok = query->getData(1, gameID, GAMEID_SIZE);
				ok = ok && query->getData(2, completed);
				ok = ok && query->getData(3, &totalBet, ODBCQuery::MONEY);
				ok = ok && query->getData(4, &cardsPlayed);
                ok = ok && query->getData(5, &multiplierValue );
                ok = ok && query->getData(6, &multipliedCardID );
			}

            if (ok)
            {
                game = new BonusBingoGame( totalBet / cardsPlayed, multiplierValue );
            }
		}
		delete query;
	}

    //Now get the saved cards...
    if (!*completed &&  ok )
    {
        ok = false;
        query = conn->query();
        if (query)
        {
            query->define("exec pub_BonusBingoGetCards ");
            query->addParam( gameID );

			ok = query->exec();
            if ( ok )
            {
                int32 cardID = 0;
                int32 cardBet = 0;
                uint8 cellValues[BonusBingo::NUMBER_OF_PICKS];

                for( int card = 0 ; ok && card < BonusBingo::NUM_MAX_CARDS; ++card )
                {
					ok = query->getRow();
                    if ( ok )
                    {
                        BonusBingoCard* pCard = new BonusBingoCard();

                        game->addCard( card, pCard );

                        ok = query->getData( 1, &cardID );

                        if ( ok )
                        {
                            pCard->setID( cardID );

                            //Set pick values and active state
                            //Active means a non-zero bet was placed
                            ok = query->getData(2, &cardBet, ODBCQuery::MONEY);
                        }

                        if ( ok )
                        {
                            pCard->activate( cardBet != 0 );
                        }                        

                        memset( cellValues, 0, sizeof(cellValues) );

                        //The db doesn't store anything for the "wild" cell
                        //but if we only ignore it at the db level it saves a lot
                        //of headache everywhere else.  So, we'll need to account
                        //for the fact that our array offsets will get out of sync
                        int offset = 0;
                        
                        for( int index = 0 ; ok && index < BonusBingoCard::NUM_BINGO_PICKS; ++index )
                        {
                            if ( index != BonusBingoCard::NUM_WILD_INDEX )
                            {
                                ok = query->getData( 3 + index - offset, &cellValues[index] );
                            }
                            else
                            {
                                offset = 1;
                            }
                        }

                        if ( ok )
                        {
                            pCard->setValues( cellValues );
                        }
                    }
                }
            }
        }
        delete query;
    }
    
    //Now that we've got the game, we can do this
    if (!*completed && ok && game && multipliedCardID )
        game->setMultipliedCardID( multipliedCardID );

	delete conn;

	return ok ? TrackSQL::NoError : TrackSQL::ExecFailed;
}

int32 BonusBingoSQL::getSavedSubGame(const PID& pid, bool8 forMoney, BonusBingoSubGame*& subGameData)
{
    ODBCConnection* conn;
	ODBCQuery* query;

	bool8 ok = false;

    if ( !subGameData )
    {
        subGameData = new BonusBingoSubGame( pid, forMoney );
    }

	conn = tracker->getConn();
	
    if (!conn)
		return TrackSQL::NoConnection;    

	query = conn->query();
	if (query)
	{
        query->define("exec pub_BonusBingoGetSubGameData ");
        query->addParam( pid );
        query->addParam( forMoney );
		ok = query->exec();
        if ( ok )
        {
            uint8 id;
            int32 data;
            bool8 serverOnly;

            while ( ok && query->getRow() )
            {
                ok = query->getData(1, &id);

                if ( ok )
                {
                    ok = query->getData(2, &data);
                }
                else
                {
                /**
                * Problem with Id.
                    */
                    ASSERT(0);
                }
                
                if ( ok )
                {
                    ok = query->getData(3, &serverOnly);
                }
                else
                {
                /**
                * Problem with Data.
                    */
                    ASSERT(0);
                }
                                
                if ( ok )
                {
                    subGameData->getSubGameData().setServerOnly(id, serverOnly);
                    subGameData->getSubGameData().update(id, data);
                }
                else
                {
                /**
                * Problem with serverOnly.
                    */
                    ok = false;
                }                
            }
        }
        delete query;
    }
    delete conn;

	return ok ? TrackSQL::NoError : TrackSQL::ExecFailed;
}

int32 BonusBingoSQL::updateSubGame(int32 casinoID, SESSIONID sessionID,
                                   bool8 forMoney, BonusBingoSubGame* subGameData)
{
    bool8 ok = true;
	ODBCConnection* conn;
	ODBCQuery* query;

	conn = tracker->getConn();
	if (conn)
    {
        SubGame& subGame = subGameData->getSubGameData();

        for ( int i = 0; ok && i < subGame.getDataCount(); i++ )
        {
            query = conn->query();
            if (query)
            {
                query->define("exec pub_BonusBingoSetSubGameData ");
                query->addParam(subGame.getPid());
                query->addParam((bool8)(subGame.isForMoney() != 0));
                query->addParam(subGame.getIdAt(i));
                query->addParam(subGame.getDataAt(i));
                query->addParam(subGame.isServerOnlyAt(i));

				ok = query->exec();
                if (!ok)
                {
                    /**
                     * Unable to execute query.
                     */
                    ASSERT(!"Unable to execute query");
                }

                delete query;
            }           
        }

        //If the dice have been rolled, record the outcome to the
        //dice rounds table as well.
        if ( subGameData->getCurrentRoll() != 0 )
        {
            ASSERT( subGameData->getGameID() != NULL );
            query = conn->query();
            if ( query )
            {
                query->define("exec pub_BonusBingoCreateDiceRound ");
                query->addParam(casinoID);
                query->addParam(subGameData->getGameID(), ODBCQuery::NOQUOTES);
                query->addParam(subGame.getPid());
                query->addParam((bool8)(forMoney != 0));
                query->addParam(sessionID, ODBCQuery::NOQUOTES);
                query->addParam(subGameData->getCurrentRoll());
                for( int die = 0 ; die < 5; ++die )
                {
                    query->addParam(subGameData->getCurrentDieValue(die));
                }
                query->addParam(subGameData->getAverageBetMultiplier(), ODBCQuery::MONEY);
                query->addParam(subGameData->getCreditsForLastRoll(), ODBCQuery::MONEY);
                
                ok = query->exec();
            }
            delete query;
        }

    	delete conn;
    }
    else
    {
        return TrackSQL::NoConnection; 
    }

	return ok ? TrackSQL::NoError : TrackSQL::ExecFailed;
}
